#pragma once
#ifndef OBR_SHADER_INTERSECTIONTEST_CUH
#define OBR_SHADER_INTERSECTIONTEST_CUH

#include "../Utils.cuh"

namespace OBR
{
static __forceinline__ __device__ void intersectTr(SamplePRD *prd, Spectrum &tr)
{
    const uint med_base = prd->params->medium_base_offset;

    uint p0, p1;
    packPointer(prd, p0, p1);
    tr = Spectrum(1.0f);

    prd->hit = false;

    // loop trace
    const uint max_depth = 16;
    uint depth = 0;
    while (true)
    {
        depth++;
        optixTrace(                        // trace new ray
            prd->params->handle,           // handel
            prd->inter.wi.o,               // ray origin
            prd->inter.wi.d,               // ray direction
            prd->inter.wi.tmin,            // Tmin
            prd->inter.wi.tmax,            // Tmax
            prd->scene_time,               // scene T
            OptixVisibilityMask(1),        // vis mask
            OPTIX_RAY_FLAG_DISABLE_ANYHIT, // ray flags
            0,                             // SBT offset
            1,                             // SBT stride
            0,                             // miss sbt
            p0, p1);

        // accumulate tr
        if (prd->consider_medium && prd->inter.wo.med_id != -1)
        {
            tr *= optixDirectCall<Spectrum, SamplePRD *, MediumCallMode>(
                prd->inter.wo.med_id + med_base, prd, MediumCallMode::CALC_TR);
        }

        // break if miss
        if (!prd->hit)
            break;

        // break when found solid surface
        if (prd->inter.mat_id != -1)
            break;

        // break when tr is 0
        if (tr.isValid())
            break;

        // break when depth is too large
        if (depth > max_depth)
        {
            tr = Spectrum(0.0f);
            break;
        }

        // if not breaking, generate new ray
        prd->inter.wi = prd->inter.spawnRay(prd->inter.wo.d);
        prd->inter.wi.tmax = prd->inter.wo.tmax - prd->inter.wo.t;
    }
}
} // namespace OBR

#endif
